package job

import (
	"context"
	"fmt"
	"gcrontab/common"
	"github.com/coreos/etcd/clientv3"
)

//分布式锁 （TXN事物）
type JobLock struct {
	kv         clientv3.KV
	lease      clientv3.Lease
	jobName    string
	cancelFunc context.CancelFunc
	leaseId    clientv3.LeaseID
	isLocked   bool
}

//初始化
func InitJobLock(jobName string, kv clientv3.KV, lease clientv3.Lease) (jobLock *JobLock) {
	jobLock = &JobLock{
		kv:      kv,
		lease:   lease,
		jobName: jobName,
	}
	return
}

//尝试上锁
func (j *JobLock) TryLock() (err error) {
	var (
		txn     clientv3.Txn
		lockKey string
		txnResp *clientv3.TxnResponse
	)
	//1，创建租约 5s， 类似于5s一次心跳
	leaseGrantResp, err := j.lease.Grant(context.TODO(), 5)
	if err != nil {
		return
	}
	leaseId := leaseGrantResp.ID //租约ID

	fmt.Println("租约ID", leaseId)

	//2，创建cancelFunc 用于取消自动续租
	ctx, cancelFunc := context.WithCancel(context.TODO())

	//3，自动续租
	keepRespChan, err := j.lease.KeepAlive(ctx, leaseId)
	if err != nil {
		goto FAIL

	}

	//4，处理自动续约应答
	go func() {
		for {
			select {
			case keepResp := <-keepRespChan:
				if keepResp == nil {
					fmt.Printf("已经关闭续租功能\n")
					goto END
				} else {
					fmt.Printf("续租成功\n")
				}

			}
		}
	END:
	}()

	//5，创建事物txn
	txn = j.kv.Txn(context.TODO())
	lockKey = common.JOB_LOCK_DIR + j.jobName
	//6，事物抢锁
	txn.If(clientv3.Compare(clientv3.CreateRevision(lockKey), "=", 0)).
		Then(clientv3.OpPut(lockKey, "", clientv3.WithLease(leaseId))).
		Else(clientv3.OpGet(lockKey))

	//7，提交事物
	txnResp, err = txn.Commit()
	if err != nil {
		goto FAIL
	}

	//8，成功则返回，失败释放租约
	//抢锁失败，锁被占用
	if !txnResp.Succeeded {
		err = common.ERR_JOB_LOCK
		goto FAIL
	}
	//抢锁成功
	j.leaseId = leaseId
	j.cancelFunc = cancelFunc
	j.isLocked = true
	return
FAIL:
	fmt.Println("续租失败")
	cancelFunc()                            //取消自动续租
	j.lease.Revoke(context.TODO(), leaseId) //释放租约 todo 租约释放失败处理？
	return
}

//释放租约
func (j *JobLock) UnLock() {
	fmt.Println("释放租约", j.isLocked, j.leaseId, j.jobName)
	if j.isLocked {
		j.cancelFunc()
		j.lease.Revoke(context.TODO(), j.leaseId)
	}
}
